package tester;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import src.ClosureType;
import src.Concept;
import src.Context;

public class ScifiExampleTester {
	
	public static void main(String[] args) {
//		studenti po riadkoch	- objekty B1
//		predmety po stlpcoch	- atributy A1
		
//		studenti utriedeni podla typu strednej skoly
		
//		mat-fyz, jazyky, chem-bio, humanit.
		Double[][] relation1 = {{ 1.0, 1.0, 1.0, 1.0 },				
								{ 1.0, 1.0, 0.5, 0.5 }};		//gymnazia
								
		Double[][] relation2 = {{ 0.5, 0.5, 0.5, 0.5 },
								{ 1.0, 0.5, 0.5, 0.0 }};		//obchodne akademie
								
		Double[][] relation3 = {{ 1.0, 0.5, 0.0, 0.0 },
								{ 0.5, 0.0, 0.0, 0.0 }};		//stredne priemyselne skoly

		
//		preferencie (hodnotenia v predmetoch) po riadkoch	- objekty B2
//		vysoke skoly po stlpcoch							- atributy A2
		
//		vysoke skoly utriedene podla miesta sidla
		
//		technika, pravo, medina
		Double[][] relation4 = {{ 1.0, 0.0, 0.0 },			
								{ 0.5, 1.0, 1.0 },		//KE		
								{ 0.5, 0.0, 1.0 },
								{ 0.0, 1.0, 0.0 }};
		
		Double[][] relation5 = {{ 1.0, 0.0, 0.0 },			
								{ 0.5, 1.0, 1.0 },		//BA		
								{ 0.5, 0.0, 1.0 },
								{ 0.0, 1.0, 0.0 }};
		
		Double[][] relation6 = {{ 1.0, 0.0, 0.0 },			
								{ 0.5, 1.0, 1.0 },		//BB		
								{ 0.5, 0.0, 1.0 },
								{ 0.0, 1.0, 0.0 }};
		
		Context c1 = new Context(3, relation1);
		Context c2 = new Context(3, relation2);
		Context c3 = new Context(3, relation3);	
		Context c4 = new Context(3, relation4);	
		Context c5 = new Context(3, relation5);	
		Context c6 = new Context(3, relation6);
		
		Context directProductC1C4 = directProduct(c1, c4);
		for (Double[] row : directProductC1C4.getRelation()) {
			for (Double value : row) {
				System.out.print(value + ", ");
			}
			System.out.println();
		}		
		System.out.println();
		
		
		Context directProductC1C5 = directProduct(c1, c5);
		for (Double[] row : directProductC1C5.getRelation()) {
			for (Double value : row) {
				System.out.print(value + ", ");
			}
			System.out.println();
		}		
		System.out.println();
		
		Context directProductC1C6 = directProduct(c1, c6);
		for (Double[] row : directProductC1C6.getRelation()) {
			for (Double value : row) {
				System.out.print(value + ", ");
			}
			System.out.println();
		}		
		System.out.println();
		
		Context directProductC2C4 = directProduct(c2, c4);
		for (Double[] row : directProductC2C4.getRelation()) {
			for (Double value : row) {
				System.out.print(value + ", ");
			}
			System.out.println();
		}		
		System.out.println();
		
		Context directProductC2C5 = directProduct(c2, c5);
		for (Double[] row : directProductC2C5.getRelation()) {
			for (Double value : row) {
				System.out.print(value + ", ");
			}
			System.out.println();
		}		
		System.out.println();
		
		Context directProductC2C6 = directProduct(c2, c6);
		for (Double[] row : directProductC2C6.getRelation()) {
			for (Double value : row) {
				System.out.print(value + ", ");
			}
			System.out.println();
		}		
		System.out.println();
		
		Context directProductC3C4 = directProduct(c3, c4);	
		for (Double[] row : directProductC3C4.getRelation()) {
			for (Double value : row) {
				System.out.print(value + ", ");
			}
			System.out.println();
		}		
		System.out.println();
		
		Context directProductC3C5 = directProduct(c3, c5);
		for (Double[] row : directProductC3C5.getRelation()) {
			for (Double value : row) {
				System.out.print(value + ", ");
			}
			System.out.println();
		}		
		System.out.println();
		
		Context directProductC3C6 = directProduct(c3, c6);
		for (Double[] row : directProductC3C6.getRelation()) {
			for (Double value : row) {
				System.out.print(value + ", ");
			}
			System.out.println();
		}		
		System.out.println();
		
//		studenti - vysoke skoly - kam chce dany student ist na skolu
//		studenti utriedeni podla typu strednej skoly.
//		vysoke skoly utriedene podla miesta sidla
		
		Double[][] R1 = {{ 1.0, 0.5, 1.0 },		//studenti: gymnazia		
						{ 1.0, 1.0, 1.0 }};		//vysoke skoly: BA		
				
		Double[][] R2 = {{ 1.0, 1.0, 1.0 },		//studenti: gymnazia
						{ 1.0, 0.5, 0.0 }};		//vysoke skoly: KE
						
		Double[][] R3 = {{ 1.0, 0.5, 0.0 },		//studenti: gymnazia
						{ 1.0, 0.5, 1.0 }};		//vysoke skoly: BB		
		
		Double[][] R4 = {{ 1.0, 0.0, 0.5 },		//studenti: obchodne akademie	
						{ 1.0, 0.0, 0.5 }};		//vysoke skoly: BA		
		
		Double[][] R5 = {{ 1.0, 0.5, 0.5 },		//studenti: obchodne akademie	
						{ 0.5, 0.0, 0.0 }};		//vysoke skoly: KE		
		
		Double[][] R6 = {{ 1.0, 1.0, 0.0 },		//studenti: obchodne akademie	
						{ 0.5, 0.5, 0.5 }};		//vysoke skoly: BB			
		
		Double[][] R7 = {{ 0.0, 1.0, 0.0 },		//studenti: stredne priemyselne skoly	
						{ 0.5, 0.0, 1.0 }};		//vysoke skoly: BA	

		Double[][] R8 = {{ 0.0, 1.0, 0.0 },		//studenti: stredne priemyselne skoly	
						{ 0.0, 0.5, 0.5 }};		//vysoke skoly: KE		
		
		Double[][] R9 = {{ 0.0, 1.0, 0.0 },		//studenti: stredne priemyselne skoly	
						{ 0.5, 0.0, 1.0 }};		//vysoke skoly: BB				
		
		List<Double> vectorOfObjectsR1 = getVectorOfObjects(R1);
		System.out.println("bond 1:");
		System.out.println(vectorOfObjectsR1);				
		List<Double> closureOfVectorOfObjectsR1 = closureOfVectorOfObjects(vectorOfObjectsR1, directProductC1C4, ClosureType.UP_DOWN);
		System.out.println(closureOfVectorOfObjectsR1);
		
		List<Double> vectorOfObjectsR2 = getVectorOfObjects(R2);
		System.out.println("bond 2:");
		System.out.println(vectorOfObjectsR2);
		List<Double> closureOfVectorOfObjectsR2 = closureOfVectorOfObjects(vectorOfObjectsR2, directProductC1C5, ClosureType.UP_DOWN);
		System.out.println(closureOfVectorOfObjectsR2);		
		
		List<Double> vectorOfObjectsR3 = getVectorOfObjects(R3);
		System.out.println("bond 3:");
		System.out.println(vectorOfObjectsR3);				
		List<Double> closureOfVectorOfObjectsR3 = closureOfVectorOfObjects(vectorOfObjectsR3, directProductC1C6, ClosureType.UP_DOWN);
		System.out.println(closureOfVectorOfObjectsR3);
		
		List<Double> vectorOfObjectsR4 = getVectorOfObjects(R4);
		System.out.println("bond 4:");
		System.out.println(vectorOfObjectsR4);		
		List<Double> closureOfVectorOfObjectsR4 = closureOfVectorOfObjects(vectorOfObjectsR4, directProductC2C4, ClosureType.UP_DOWN);
		System.out.println(closureOfVectorOfObjectsR4);
		
		List<Double> vectorOfObjectsR5 = getVectorOfObjects(R5);
		System.out.println("bond 5:");
		System.out.println(vectorOfObjectsR5);
		List<Double> closureOfVectorOfObjectsR5 = closureOfVectorOfObjects(vectorOfObjectsR5, directProductC2C5, ClosureType.UP_DOWN);
		System.out.println(closureOfVectorOfObjectsR5);
		
		List<Double> vectorOfObjectsR6 = getVectorOfObjects(R6);
		System.out.println("bond 6:");
		System.out.println(vectorOfObjectsR6);
		List<Double> closureOfVectorOfObjectsR6 = closureOfVectorOfObjects(vectorOfObjectsR6, directProductC2C6, ClosureType.UP_DOWN);
		System.out.println(closureOfVectorOfObjectsR6);
		
		List<Double> vectorOfObjectsR7 = getVectorOfObjects(R7);
		System.out.println("bond 7:");
		System.out.println(vectorOfObjectsR7);
		List<Double> closureOfVectorOfObjectsR7 = closureOfVectorOfObjects(vectorOfObjectsR7, directProductC3C4, ClosureType.UP_DOWN);
		System.out.println(closureOfVectorOfObjectsR7);
		
		List<Double> vectorOfObjectsR8 = getVectorOfObjects(R8);
		System.out.println("bond 8:");
		System.out.println(vectorOfObjectsR8);
		List<Double> closureOfVectorOfObjectsR8 = closureOfVectorOfObjects(vectorOfObjectsR8, directProductC3C5, ClosureType.UP_DOWN);
		System.out.println(closureOfVectorOfObjectsR8);
		
		List<Double> vectorOfObjectsR9 = getVectorOfObjects(R9);
		System.out.println("bond 9:");
		System.out.println(vectorOfObjectsR9);	
		List<Double> closureOfVectorOfObjectsR9 = closureOfVectorOfObjects(vectorOfObjectsR9, directProductC3C6, ClosureType.UP_DOWN);
		System.out.println(closureOfVectorOfObjectsR9);		
//		Double[][] relation7 = {{ 1.0, 1.0 },			
//								{ 0.0, 0.0 }};
//
//		Double[][] relation8 = {{ 0.0 },			
//								{ 1.0 },
//								{ 1.0 }};
//		
//		double[][] directProductRelation = new double[relation7.length * relation8[0].length][relation7[0].length * relation8.length];
//		int p = 0;
//		int q = 0;		
//		for (int i = 0; i < relation7.length; i++) {
//			for (int j = 0; j < relation8[0].length; j++) {
//				for (int k = 0; k < relation8.length; k++) {
//					for (int l = 0; l < relation7[0].length; l++) {						
//						directProductRelation[p][q] = disjunction(relation7[i][l], relation8[k][j]);
//						q++;
//					}
//				}
//				p++;
//				q = 0;
//				
//			}
//		}
//		
//		for (double[] row : directProductRelation) {
//			for (Double value : row) {
//				System.out.print(value + ", ");
//			}
//			System.out.println();
//		}		
//		System.out.println();
//		
//		Context c7 = new Context(2, relation7);	
//		Context c8 = new Context(2, relation8);
//
//		Context directProduct = directProduct(c7, c8);
//		for (Double[] row : directProduct.getRelation()) {
//			for (Double value : row) {
//				System.out.print(value + ", ");
//			}
//			System.out.println();
//		}
	}
	
//	globalContext.. vediet pristupovat ku bondom Ci,j
//	pripravit relaciu z uz ziskanych bondov - globalContext

	private static List<Concept> specialDown(Context globalContext, List<Concept> concepts, List<Context> contextsO, List<Context> contextsA) {
		List<Concept> result = new ArrayList<Concept>();
		
		for (int i = 0; i < globalContext.getObjectsCount(); i++) {			
			Context contextO = contextsO.get(i);
			List<Double> infimum = new ArrayList<Double>();
			for (int j = 0; j < contextO.getAttributesCount(); j++) {
				infimum.add(1.0);
			}			
			
			for (int j = 0; j < globalContext.getAttributesCount(); j++) {
//				globalContext.getBond(j, i);	vrati mi bond o a
				List<List<Double>> bond = new ArrayList<List<Double>>();
				List<Double> prechodR = prechodR(bond, concepts.get(j), contextsO.get(i), contextsA.get(j));
				infimum = infimum(infimum, prechodR);
			}
			
			result.add(new Concept(contextsO.get(i).down(infimum), infimum));
		}
		
		return result;
	}
	
	private static List<Concept> specialUp(Context globalContext, List<Concept> concepts, List<Context> contextsO, List<Context> contextsA) {
		List<Concept> result = new ArrayList<Concept>();
		
		for (int i = 0; i < globalContext.getAttributesCount(); i++) {			
			Context contextA = contextsA.get(i);
			List<Double> infimum = new ArrayList<Double>();
			for (int j = 0; j < contextA.getObjectsCount(); j++) {
				infimum.add(1.0);
			}			
			
			for (int j = 0; j < globalContext.getObjectsCount(); j++) {
//				globalContext.getBond(j, i);	vrati mi bond o a
				List<List<Double>> bond = new ArrayList<List<Double>>();
				List<Double> prechodL = prechodL(bond, concepts.get(j), contextsO.get(j), contextsA.get(i));
				infimum = infimum(infimum, prechodL);
			}
			
			result.add(new Concept(infimum, contextsA.get(i).up(infimum)));
		}
		
		return result;
	}
	
	private static List<Double> infimum(List<Double> fuzzySet1, List<Double> fuzzySet2) {
		List<Double> result = new ArrayList<Double>();
		for (int i = 0; i < fuzzySet1.size(); i++) {
			result.add(Math.min(fuzzySet1.get(i), fuzzySet2.get(i)));
		}		
		return result;
	}
	
	private static List<Double> prechodR(List<List<Double>> bond, Concept concept, Context contextO, Context contextA) {
		List<Double> fuzzySetOfAttributesB = new ArrayList<Double>();
		List<Double> intent = concept.getIntent();
		List<List<Double>> rightCorrespondence = rightCorrespondence(bond, contextO, contextA);
		
		for (int i = 0; i < contextO.getAttributesCount(); i++) {
			double pom = 0.0;
			
			for (int j = 0; j < contextA.getAttributesCount(); j++) {
				Double conjunction = silnaKonjunkcia(rightCorrespondence.get(j).get(i), intent.get(j));
				pom = Math.max(pom, conjunction);				
			}
			fuzzySetOfAttributesB.add(pom);
		}
		return contextO.closure(fuzzySetOfAttributesB, ClosureType.DOWN_UP);
	}	//vrati intent
	
	private static List<Double> prechodL(List<List<Double>> bond, Concept concept, Context contextO, Context contextA) {
		List<Double> fuzzySetOfObjectsA = new ArrayList<Double>();
		List<Double> extent = concept.getExtent();
		List<List<Double>> leftCorrespondence = leftCorrespondence(bond, contextO, contextA);
		
		for (int i = 0; i < contextA.getObjectsCount(); i++) {
			double pom = 0.0;
			
			for (int j = 0; j < contextO.getObjectsCount(); j++) {
				Double conjunction = silnaKonjunkcia(leftCorrespondence.get(j).get(i), extent.get(j));
				pom = Math.max(pom, conjunction);				
			}
			fuzzySetOfObjectsA.add(pom);
		}
		return contextA.closure(fuzzySetOfObjectsA, ClosureType.UP_DOWN);
	}	//vrati extent
	
	private static Double silnaKonjunkcia(Double a, Double b) { 
		return Math.max(0.0, a + b - 1.0);
	}
	
	private static List<List<Double>> leftCorrespondence(List<List<Double>> bond, Context contextO, Context contextA) {
		List<List<Double>> result = new ArrayList<List<Double>>();
		
		for (int i = 0; i < contextO.getObjectsCount(); i++) {
			result.add(contextA.down(bond.get(i)));
		}
		
		return result;
	}
	
	private static List<List<Double>> rightCorrespondence(List<List<Double>> bond, Context contextO, Context contextA) {
		List<List<Double>> result = new ArrayList<List<Double>>();
		
		List<List<Double>> trBond = transpose(bond);
		for (int i = 0; i < contextA.getAttributesCount(); i++) {
			result.add(contextO.up(trBond.get(i)));
		}
		
		return result;
	}
	
    private static List<List<Double>> transpose(List<List<Double>> fuzzySets) {
    	List<List<Double>> result = new ArrayList<List<Double>>();
    	
    	for (int i = 0; i < fuzzySets.get(0).size(); i++) {
    		List<Double> temp = new ArrayList<Double>();
    		for (int j = 0; j < fuzzySets.size(); j++) {
    			temp.add(fuzzySets.get(j).get(i));
			}
    		result.add(temp);
		}

    	return result;
    }
		
	private static List<Double> closureOfVectorOfObjects(List<Double> vectorOfObjects, Context context, ClosureType closureType) {
		List<Double> closureOfVectorOfObjects = context.closure(vectorOfObjects, closureType);
		return closureOfVectorOfObjects;
	}
	
	private static List<Double> getVectorOfObjects(Double[][] relation) {
		Double[] vectorOfObjects = new Double[relation.length * relation[0].length];
		int p = 0;
		for (int i = 0; i < relation.length; i++) {
			for (int j = 0; j < relation[0].length; j++) {
				vectorOfObjects[p] = relation[i][j];
				p++;
			}
		}
		
		return Arrays.asList(vectorOfObjects);
	}

	private static Context directProduct(Context context1, Context context2) {
		Context directProduct = null;
		Integer logicDegree1 = context1.getLogic().getLogicDegree();
		Integer logicDegree2 = context2.getLogic().getLogicDegree();
		
		if (logicDegree1 != logicDegree2) {
			System.err.println("It is not possible to do direct product of contexts with different logic degree");
		}
		
		Double[][] relation1 = context1.getRelation();
		Double[][] relation2 = context2.getRelation();
		
		Double[][] directProductRelation = new Double[relation1.length * relation2[0].length][relation1[0].length * relation2.length];
		
		int p = 0;
		int q = 0;		
		for (int i = 0; i < relation1.length; i++) {
			for (int j = 0; j < relation2[0].length; j++) {
				for (int k = 0; k < relation2.length; k++) {
					for (int l = 0; l < relation1[0].length; l++) {						
						directProductRelation[p][q] = disjunction(relation1[i][l], relation2[k][j]);
						q++;
					}
				}
				p++;
				q = 0;				
			}
		}
		
		directProduct = new Context(logicDegree1, directProductRelation);
		
		return directProduct;
	}
		
	private static Double disjunction(Double firstValue, Double secondValue) {
		return implication(implication(firstValue, 0.0), secondValue);
	}
	
	private static Double implication(Double a, Double b) { 
		return Math.min(1.0 - a + b, 1.0);
	}
	
//	private static Context directProduct(Context context1, Context context2) {
//		Context directProduct = null;
//		Integer logicDegree1 = context1.getLogic().getLogicDegree();
//		Integer logicDegree2 = context2.getLogic().getLogicDegree();
//		
//		if (logicDegree1 != logicDegree2) {
//			System.err.println("It is not possible to do direct product of contexts with different logic degree");
//		}
//		
//		Double[][] relation1 = context1.getRelation();
//		Double[][] relation2 = context2.getRelation();
//		
//		List<List<Integer>> relation1Variations = generateVariations(relation1[0].length - 1, 2);
//		List<List<Integer>> relation2Variations = generateVariations(relation2[0].length - 1, 2);
//		
//		for (List<Integer> list : relation1Variations) {
//			System.out.println(list);
//		}
//		System.out.println();
//		
//		for (List<Integer> list : relation2Variations) {
//			System.out.println(list);
//		}
//		System.out.println();
//		
//		Double[][] directProductRelation = new Double[relation1Variations.size()][relation2Variations.size()];
//		
//		
//		for (int i = 0; i < relation1Variations.size(); i++) {
//			for (int j = 0; j < relation2Variations.size(); j++) {
//				List<Integer> relation1Pair = relation1Variations.get(i);
//				List<Integer> relation2Pair = relation2Variations.get(j);
//				Double firstValue = relation1[relation1Pair.get(0)][relation1Pair.get(1)];
//				Double secondValue = relation2[relation2Pair.get(0)][relation2Pair.get(1)];
//				System.out.println(relation1Pair + " <--> " + relation2Pair + " - " + firstValue + " <--> " + secondValue + " = " + disjunction(firstValue, secondValue));
//				directProductRelation[i][j] = disjunction(firstValue, secondValue);   
//			}
//		}
//		
//		directProduct = new Context(logicDegree1, directProductRelation);
//		
//		return directProduct;
//	}

//	private static Double disjunction(Double firstValue, Double secondValue) {
//		Integer result = firstValue.intValue() | secondValue.intValue();
//		return result.doubleValue();
//	}
	
//    private static void generate(int odIndexu, List<List<Integer>> variations, List<Integer> variation, int n, int k) {
//        if (odIndexu == k) {
//            variations.add(new ArrayList<Integer>(variation));
//            return;
//        }
//
//    	for (int i = 0; i <= n; i++) {
//    		variation.set(odIndexu, i);
//    		generate(odIndexu + 1, variations, variation, n, k);                    
//        }
//    }
//
//    private static List<List<Integer>> generateVariations(int n, int k) {
//    	List<List<Integer>> variations = new ArrayList<List<Integer>>();
//    	List<Integer> variation = Arrays.asList(new Integer[k]);
//        generate(0, variations, variation, n, k);
//        return variations;
//    }
}
